/*
 * **************************************************************************
 * *                                                                        *
 * * Ericsson hereby grants to the user a royalty-free, irrevocable,        *
 * * worldwide, nonexclusive, paid-up license to copy, display, perform,    *
 * * prepare and have prepared derivative works based upon the source code  *
 * * in this sample application, and distribute the sample source code and  *
 * * derivative works thereof and to grant others the foregoing rights.     *
 * *                                                                        *
 * * ERICSSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,        *
 * * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.       *
 * * IN NO EVENT SHALL ERICSSON BE LIABLE FOR ANY SPECIAL, INDIRECT OR      *
 * * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS    *
 * * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE  *
 * * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE *
 * * OR PERFORMANCE OF THIS SOFTWARE.                                       *
 * *                                                                        *
 * **************************************************************************
 */

package chat;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.ericsson.hosasdk.api.TpAddress;
import com.ericsson.hosasdk.api.hmm.TpHosaUserStatus;
import com.ericsson.hosasdk.api.hmm.TpHosaUserStatusIndicator;
import com.ericsson.hosasdk.api.hmm.hus.IpAppHosaUserStatus;
import com.ericsson.hosasdk.api.hmm.hus.IpAppHosaUserStatusAdapter;
import com.ericsson.hosasdk.api.hmm.hus.IpHosaUserStatus;
import com.ericsson.hosasdk.utility.sync.Synchronizer;
import com.ericsson.nrgsdk.examples.tools.SDKToolkit;

public class UserStatusProcessor extends IpAppHosaUserStatusAdapter implements
		IpAppHosaUserStatus {

	private IpHosaUserStatus itsHosaUSManager;

	private Synchronizer itsSynchronizer = new Synchronizer();

	private SessionHandler itsSessionHandler = new SessionHandler();

	/**
	 * @param aHosaUSManager
	 *            manager used to talk to the NRG
	 */
	public UserStatusProcessor(IpHosaUserStatus aHosaUSManager) {
		itsHosaUSManager = aHosaUSManager;
	}

	/**
	 * @return a subscriber from a given set that is reachable. If multiple
	 * subscribers are reachable, one is picked. If no subscribers area
	 * reachable, null is returned.
	 * 
	 * @param anAddresses
	 *            the set of subscribers from which a reachable subscriber is to
	 *            be determined
	 */
	public String findReachableAddress(String[] anAddresses) {
		TpAddress[] addresses = new TpAddress[anAddresses.length];
		for (int i = 0; i != addresses.length; i++) {
			addresses[i] = SDKToolkit.createTpAddress(anAddresses[i]);
		}
		int assignmentId = itsHosaUSManager
				.hosaStatusReportReq(this, addresses);
		itsSessionHandler.createSession(assignmentId, anAddresses);
		int allowedDelay = 10000 * addresses.length;
		String reachableAddress = (String) itsSynchronizer.waitForResult(
				assignmentId, allowedDelay);
		itsSessionHandler.disposeSession(assignmentId);
		return reachableAddress;
	}

	/**
	 * Called by the Ericsson Network Resource Gateway when a Status is received
	 * 
	 * @see com.ericsson.hosasdk.api.hmm.hus.IpAppHosaUserStatus
	 */
	public void hosaStatusReportRes(int anAssignmentId,
			TpHosaUserStatus[] aStatusList) {
		itsSessionHandler.processStatusReports(anAssignmentId, aStatusList,
				itsSynchronizer);
	}
}

class SessionHandler {

	private Map theSessions = new HashMap();

	void createSession(int anAssignmentId, String[] anAddresses) {
		Session session = new Session(anAssignmentId, anAddresses);
		theSessions.put(new Integer(anAssignmentId), session);
	}

	void disposeSession(int anAssignmentId) {
		theSessions.remove(new Integer(anAssignmentId));
	}

	private Session findSession(int anAssignmentId) {
		Object key = new Integer(anAssignmentId);
		Session session = (Session) theSessions.get(key);
		if (session == null) {
			// In an unlikely scenario, the Ericsson Network Resource
			// Gateway has returned a response
			// before the session has been stored. Therefore, a retry
			// is done:
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			session = (Session) theSessions.get(key);
		}
		return session;
	}

	void processStatusReports(int anAssignmentId,
			TpHosaUserStatus[] aStatusList, Synchronizer aSync) {
		Session session = findSession(anAssignmentId);
		if (session != null) {
			session.processStatusReports(anAssignmentId, aStatusList, aSync);
		}
	}

	private static class Session {

		private Set unknownUsers;

		private Session(int anAssignmentId, String[] anAddresses) {
			unknownUsers = new HashSet();
			for (int i = 0; i != anAddresses.length; i++) {
				unknownUsers.add(anAddresses[i]);
			}
		}

		void processStatusReports(int anAssignmentId,
				TpHosaUserStatus[] aStatusList, Synchronizer aSync) {
			// remove the users in the response from the set of
			// users for which no status is known, and remember
			// if there's at least one reachable user.
			String reachableUser = null;
			for (int i = 0; i != aStatusList.length; i++) {
				String user = aStatusList[i].UserID.AddrString;
				unknownUsers.remove(user);
				if (aStatusList[i].ServiceStatus == TpHosaUserStatusIndicator.P_HUS_REACHABLE) {
					reachableUser = user;
				}
			}
			// we're done if a reachable user has been found
			// or if all users have been found to be unreachable
			if (reachableUser != null || unknownUsers.size() == 0) {
				aSync.notifyResult(anAssignmentId, reachableUser);
			}
		}
	}
}
